package org.maxgroundstation.simulation.service;

import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ServiceReceiverUDP implements Runnable {
    
    // Data members
    private Thread receiverThread;
    private ServiceCommunicationUDP comm;
    private Map<String, ServiceDataFrame> dataFrames;
    private boolean run = false;
    
    /**
     * Constructor
     * @param comm the service UDP communication.
     */
    ServiceReceiverUDP(ServiceCommunicationUDP comm) {
        this.comm = comm;
        dataFrames = new HashMap<String, ServiceDataFrame>();
    }
    
    /**
     * Starts the server thread.
     */
    void start() {
        if ((receiverThread == null) || !receiverThread.isAlive() || !run) {
            receiverThread = new Thread(this, "udp receiver thread");
            receiverThread.start();
        }
    }
    
    /**
     * Run the server thread.
     */
    public void run() {
    	run = true;
        while (run) {
            if (comm.isConnected()) {
            	try {
            		comm.getServiceCommunication().log("UDP service waiting for datagram packet.");
            		byte[] buf = new byte[65507];
            		DatagramPacket packet = new DatagramPacket(buf, buf.length);
            		comm.getSocket().receive(packet);
                
            		comm.getServiceCommunication().log("UDP service receiving datagram packet.");
            		
            		byte[] packetData = Arrays.copyOfRange(packet.getData(), 0, packet.getLength());
            		ServiceDataFragment dataFrag = ServiceUtil.parseDataFragment(packetData);
                
            		String serviceID = dataFrag.getHeader().getServiceID();
            		
            		// Create new client ID or update existing one.
    				String clientID = dataFrag.getHeader().getClientID();
    				ServiceCommunication serviceComm = comm.getServiceCommunication();
    				if (clientID.trim().isEmpty()) {
    					clientID = serviceComm.getClientManager().createNewClient();
    					dataFrag.getHeader().setClientID(clientID);
    				}
    				else serviceComm.getClientManager().updateClient(clientID);
            		
    				// Add UDP client if there isn't one.
    				if (!comm.hasClient(clientID)) {
    					InetSocketAddress address = (InetSocketAddress) packet.getSocketAddress();
    					comm.addClient(clientID, address.getAddress(), address.getPort());
    				}
    				
            		dataFrames.put(serviceID, new ServiceDataFrame());
                
            		if (dataFrames.containsKey(serviceID)) {
            			ServiceDataFrame dataFrame = dataFrames.get(serviceID);
            			dataFrame.addDataFragment(dataFrag);
            			
            			if (dataFrame.isDataComplete()) {
            				comm.getServiceCommunication().log("Receiving full data frame");
                			//System.out.println(dataFrame.getDataString());
            				comm.getServiceCommunication().handleResponseData(dataFrame, ServiceCommunication.COMM_UDP);
                			
                			dataFrames.remove(serviceID);
            			}
            		}
            	}
            	catch (SocketException e) {
            		run = false;
            	}
            	catch (Exception e) {
            		e.printStackTrace(System.err);
            	}
            }
        }
    }
}